package org.hong.monkey.deploy.rest

import scala.util.Try

import org.hong.monkey.util.Utils

/**
 * An abstract request sent from the client in the REST application submission protocol.
 */
private[rest] abstract class SubmitRestProtocolRequest extends SubmitRestProtocolMessage {
  var clientMonkeyVersion: String = null
  protected override def doValidate(): Unit = {
    super.doValidate()
    assertFieldIsSet(clientMonkeyVersion, "clientMonkeyVersion")
  }
}

/**
 * A request to launch a new application in the REST application submission protocol.
 */
private[rest] class CreateSubmissionRequest extends SubmitRestProtocolRequest {
  var appResource: String = null
  var mainClass: String = null
  var appArgs: Array[String] = null
  var monkeyProperties: Map[String, String] = null
  var environmentVariables: Map[String, String] = null

  protected override def doValidate(): Unit = {
    super.doValidate()
    assert(monkeyProperties != null, "No Monkey properties set!")
    assertFieldIsSet(appResource, "appResource")
    assertPropertyIsSet("monkey.app.name")
    assertPropertyIsBoolean("monkey.driver.supervise")
    assertPropertyIsNumeric("monkey.driver.cores")
    assertPropertyIsNumeric("monkey.cores.max")
    assertPropertyIsMemory("monkey.driver.memory")
    assertPropertyIsMemory("monkey.executor.memory")
  }

  private def assertPropertyIsSet(key: String): Unit =
    assertFieldIsSet(monkeyProperties.getOrElse(key, null), key)

  private def assertPropertyIsBoolean(key: String): Unit =
    assertProperty[Boolean](key, "boolean", _.toBoolean)

  private def assertPropertyIsNumeric(key: String): Unit =
    assertProperty[Double](key, "numeric", _.toDouble)

  private def assertPropertyIsMemory(key: String): Unit =
    assertProperty[Int](key, "memory", Utils.memoryStringToMb)

  /** Assert that a Monkey property can be converted to a certain type. */
  private def assertProperty[T](key: String, valueType: String, convert: (String => T)): Unit = {
    monkeyProperties.get(key).foreach { value =>
      Try(convert(value)).getOrElse {
        throw new SubmitRestProtocolException(
          s"Property '$key' expected $valueType value: actual was '$value'.")
      }
    }
  }
}
